﻿// RemoteControl.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "pch.h"
#include "framework.h"
#include "RemoteControl.h"
#include "ServerSocket.h"
#include <direct.h>
#include <atlimage.h>
#include <process.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

//#pragma comment( linker, "/subsystem:windows /entry:WinMainCRTStartup" )
//#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )
//#pragma comment( linker, "/subsystem:console /entry:mainCRTStartup" )
//#pragma comment( linker, "/subsystem:console /entry:WinMainCRTStartup" )

// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

// debug
void Dump(BYTE* pData, size_t nSize)
{
	string strOut;
	for (size_t i = 0; i < nSize; i++)
	{
		char buf[8] = "";
		if (i > 0 && (i % 16 == 0))
			strOut += "\n";
		snprintf(buf, sizeof(buf), "%02X\t", pData[i] & 0xFF);
		strOut += buf;
	}
	strOut += "\n";
	OutputDebugStringA(strOut.c_str());
}


// 读取磁盘分区
int  MakeDriverInfo() { //1==>A 2==>B 3==>C ... 26==>Z
	string result;
	//通过切换26个字母盘符是否能切换成功来判断盘符存在
	for (int i = 1; i <= 26; i++)
	{
		if (_chdrive(i) == 0)   //如果切换成功
		{
			if (result.size() > 0)
				result += ',';
			result += 'A' + i - 1;
		}
	}
	result += ',';
	//发送回复消息
	CPacket pack(1, (BYTE*)result.c_str(), result.size());      //打包
	Dump((BYTE*)pack.Data(), pack.Size());
	pserver->Send(pack);
	return 0;
}


//查看指定目录下的文件
#include <stdio.h>
#include <io.h>
#include <list>


int MakeDirectoryInfo() {
	string strPath;
	//从数据包中取出文件路径信息
	if (CServerSocket::getInstance()->GetFilePath(strPath) == false) {
		OutputDebugString(_T("当前的命令，不是获取文件列表，命令解析错误！！"));
		return -1;
	}

	//切换到对应目录 判断目录是否有效
	//如果目录无效则向客户端发送一个标记“无后续”的信息包
	if (_chdir(strPath.c_str()) != 0) {
		FILEINFO finfo;
		finfo.HasNext = FALSE;
		CPacket pack(2, (BYTE*)&finfo, sizeof(finfo));
		CServerSocket::getInstance()->Send(pack);
		OutputDebugString(_T("没有权限访问目录！！"));
		return -2;
	}
	//在对应目录中找第一个文件夹 将找到的文件信息存到 _finddata_t结构体中
	//struct _finddata_t {
	//    unsigned attrib;        // 文件属性（如是否为目录）
	//    time_t time_create;     // 文件的创建时间
	//    time_t time_access;     // 文件的最后访问时间
	//    time_t time_write;      // 文件的最后修改时间
	//    long size;              // 文件大小（字节）
	//    char name[_MAX_FNAME];  // 文件或目录的名称
	//};
	_finddata_t fdata;
	int hfind = 0;
	//如果没找到第一个则向客户端发送一个标记“无后续”的信息包
	if ((hfind = _findfirst("*", &fdata)) == -1) {
		OutputDebugString(_T("没有找到任何文件！！"));
		FILEINFO finfo;
		finfo.HasNext = FALSE;
		CPacket pack(2, (BYTE*)&finfo, sizeof(finfo));
		CServerSocket::getInstance()->Send(pack);
		return -3;
	}
	//找到第一个后 采用 do-while结构便利后续的文件
	int count = 0;
	do {
		FILEINFO finfo;
		finfo.IsDirectory = (fdata.attrib & _A_SUBDIR) != 0;        //判断是否是目录
		memcpy(finfo.szFileName, fdata.name, strlen(fdata.name));   //拷贝文件名称
		TRACE("%s\r\n", finfo.szFileName);
		CPacket pack(2, (BYTE*)&finfo, sizeof(finfo));              //打包信息并发送给客户端
		CServerSocket::getInstance()->Send(pack);
		count++;
	} while (!_findnext(hfind, &fdata));
	TRACE("server: count = %d\r\n", count);
	//发送“无后续”标记信息到控制端
	FILEINFO finfo;
	finfo.HasNext = FALSE;
	CPacket pack(2, (BYTE*)&finfo, sizeof(finfo));
	CServerSocket::getInstance()->Send(pack);
	return 0;
}


// 运行文件
int RunFile() {
	string strPath;
	CServerSocket::getInstance()->GetFilePath(strPath);
	ShellExecuteA(NULL, NULL, strPath.c_str(), NULL, NULL, SW_SHOWNORMAL);          //excute file
	CPacket pack(3, NULL, 0);                                                       //应答
	CServerSocket::getInstance()->Send(pack);
	return 0;
}

// 下载文件
int DownloadFile() {
	std::string strPath;
	CServerSocket::getInstance()->GetFilePath(strPath);
	long long data = 0;
	FILE* pFile = NULL;
	errno_t err = fopen_s(&pFile, strPath.c_str(), "rb");           //以二进制方式打开
	if (err != 0) {
		CPacket  pack(4, (BYTE*)&data, 8);
		CServerSocket::getInstance()->Send(pack);
		TRACE("服务端无法打开文件！\r\n");
		return -1;
	}
	if (pFile != NULL) {
		//首先发送文件大小
		fseek(pFile, 0, SEEK_END);
		data = _ftelli64(pFile);
		CPacket head(4, (BYTE*)&data, 8);
		CServerSocket::getInstance()->Send(head);
		fseek(pFile, 0, SEEK_SET);
		//发送文件一次发送1KB 直到发送完毕
		char buffer[1024] = "";
		size_t rlen = 0;
		do {
			rlen = fread(buffer, 1, 1024, pFile);
			CPacket pack(4, (BYTE*)buffer, rlen);
			CServerSocket::getInstance()->Send(pack);
		} while (rlen >= 1024);
		fclose(pFile);
	}
	CPacket pack(4, NULL, 0);
	CServerSocket::getInstance()->Send(pack);
	return 0;
}


// 处理鼠标事件
int MouseEvent()
{
	MOUSEEV mouse;

	if (CServerSocket::getInstance()->GetMouseEvent(mouse)) {
		DWORD nFlags = 0;       //鼠标状态标志位
		//定义为DWORD 方便扩展 与Windows API兼容

//低四位用于表示鼠标按键
//高四位用于表示鼠标动作
		switch (mouse.nButton) {
		case 0://左键
			nFlags = 1;
			break;
		case 1://右键
			nFlags = 2;
			break;
		case 2://中键
			nFlags = 4;
			break;
		case 4://没有按键
			nFlags = 8;
			break;
		}
		if (nFlags != 8)SetCursorPos(mouse.ptXY.x, mouse.ptXY.y);
		switch (mouse.nAction)
		{
		case 0://单击
			nFlags |= 0x10;
			break;
		case 1://双击
			nFlags |= 0x20;
			break;
		case 2://按下
			nFlags |= 0x40;
			break;
		case 3://放开
			nFlags |= 0x80;
			break;
		default:
			break;
		}
		TRACE("mouse event : %08X x %d y %d\r\n", nFlags, mouse.ptXY.x, mouse.ptXY.y);
		switch (nFlags)
		{
		case 0x21://左键双击
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x11://左键单击
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x41://左键按下
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x81://左键放开
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x22://右键双击
			mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, GetMessageExtraInfo());
		case 0x12://右键单击
			mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x42://右键按下
			mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x82://右键放开
			mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x24://中键双击
			mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, GetMessageExtraInfo());
		case 0x14://中键单击
			mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, GetMessageExtraInfo());
			mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x44://中键按下
			mouse_event(MOUSEEVENTF_MIDDLEDOWN, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x84://中键放开
			mouse_event(MOUSEEVENTF_MIDDLEUP, 0, 0, 0, GetMessageExtraInfo());
			break;
		case 0x08://单纯的鼠标移动
			mouse_event(MOUSEEVENTF_MOVE, mouse.ptXY.x, mouse.ptXY.y, 0, GetMessageExtraInfo());
			TRACE("client mouse move %d, %d\r\n", mouse.ptXY.x, mouse.ptXY.y);
			break;
		}
		CPacket pack(5, NULL, 0);
		CServerSocket::getInstance()->Send(pack);
	}
	else
	{
		OutputDebugString(_T("获取鼠标操作参数失败！！"));
		return -1;
	}
	return 0;
}



//处理屏幕图像
//#include <atlimage.h>
int SendScreen()
{
	CImage screen;						//创建CImage对象来存储截图 该类封装了图形图像操作
										//GDI Graphic Device Interface
	HDC hScreen = ::GetDC(NULL);		//获取设备上下文 获取句柄
	int nBitPerPixel = GetDeviceCaps(hScreen, BITSPIXEL);		//获取位宽(颜色深度）  真色彩 24bit ARGB8888(A—透明度) 32bit RGB888 24bit RGB565 RGB444	  TODO: 补充位图相关知识
	int nWidth = GetDeviceCaps(hScreen, HORZRES);				//获取屏幕宽度
	int nHeight = GetDeviceCaps(hScreen, VERTRES);				//获取屏幕高度
	screen.Create(nWidth, nHeight, nBitPerPixel);				//创建对应大小和颜色深度的屏幕截图
	BitBlt(screen.GetDC(), 0, 0, nWidth, nHeight, hScreen, 0, 0, SRCCOPY);		//将屏幕截图复制到CImage的对象screen中
	ReleaseDC(NULL, hScreen);									//释放品目设备上下文句柄

	//创建内存流 将图像保存到内存中
	HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE, 0);				//分配一个全局可调整大小的内存块
	if (hMem == NULL)return -1;
	IStream* pStream = NULL;									
	HRESULT ret = CreateStreamOnHGlobal(hMem, TRUE, &pStream);	//创造一个内存流
	if (ret == S_OK) {
		screen.Save(pStream, Gdiplus::ImageFormatPNG);			//使用 GDI 将图像以 PNG 格式保存到内存流 pStream 中
		LARGE_INTEGER bg = { 0 };
		pStream->Seek(bg, STREAM_SEEK_SET, NULL);				//将流的位置指针设置为流的起始位置
		PBYTE pData = (PBYTE)GlobalLock(hMem);					//将全局内存锁定，并返回指向内存块的指针
		SIZE_T nSize = GlobalSize(hMem);						//获取全局内存块的大小
		CPacket pack(6, pData, nSize);							//将内存数据打包为 CPacket 发送给服务器
		CServerSocket::getInstance()->Send(pack);
		GlobalUnlock(hMem);										//释放对全局内存的锁定
	}
	//screen.Save(_T("test2020.png"), Gdiplus::ImageFormatPNG);			//保存图片
	/*
	//测试 png 和 jpg格式图像的生成时间区别
	TRACE("png %d\r\n", GetTickCount64() - tick);
	for (int i = 0; i < 10; i++) {
		DWORD tick = GetTickCount64();									// 获取从运行开始到当前的时间
		screen.Save(_T("test2020.png"), Gdiplus::ImageFormatPNG);
		TRACE("png %d\r\n", GetTickCount64() - tick);
		tick = GetTickCount64();
		screen.Save(_T("test2020.jpg"), Gdiplus::ImageFormatJPEG);
		TRACE("jpg %d\r\n", GetTickCount64() - tick) ;
	}*/
	pStream->Release();
	GlobalFree(hMem);
	screen.ReleaseDC();
	return 0;
}


//锁机
//基于MFC新建一个Dialog
//将其置于顶层 放到最大 取消菜单关闭按钮
#include "LockInfoDialog.h"
CLockInfoDialog dlg;
unsigned threadid = 0;

// 在子线程中锁机 防止锁机后卡在函数中 无法解锁
unsigned __stdcall threadLockDlg(void* arg)
{
	TRACE("%s(%d):%d\r\n", __FUNCTION__, __LINE__, GetCurrentThreadId());
	dlg.Create(IDD_DIALOG_INFO, NULL);
	dlg.ShowWindow(SW_SHOW);
	//将窗口放大 遮蔽后台窗口
	CRect rect;
	rect.left = 0;
	rect.top = 0;
	// 获取屏幕的宽度和高度当前品目分辨率
	rect.right = GetSystemMetrics(SM_CXFULLSCREEN);
	rect.bottom = GetSystemMetrics(SM_CYFULLSCREEN);
	//将窗口高度增加10%，确保对话框能够覆盖整个屏幕（包括任务栏）
	rect.bottom = LONG(rect.bottom * 1.10);
	TRACE("right = %d bottom = %d\r\n", rect.right, rect.bottom);
	dlg.MoveWindow(rect);

	//窗口置顶
	dlg.SetWindowPos(&dlg.wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
	//限制鼠标功能
	ShowCursor(false);
	//隐藏任务栏
	::ShowWindow(::FindWindow(_T("Shell_TrayWnd"), NULL), SW_HIDE);
	//限制鼠标活动范围
	dlg.GetWindowRect(rect);
	rect.left = 0;
	rect.top = 0;
	rect.right = 1;
	rect.bottom = 1;
	ClipCursor(rect);
	MSG msg;
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		if (msg.message == WM_KEYDOWN) {
			TRACE("msg:%08X wparam:%08x lparam:%08X\r\n", msg.message, msg.wParam, msg.lParam);
			if (msg.wParam == 0x1b) {//按下a键 退出  ESC（1B)
				break;
			}
		}
	}
	//恢复鼠标
	ClipCursor(NULL);    //解除对鼠标活动范围的限制，恢复鼠标自由移动
	ShowCursor(true);    //恢复鼠标指针的显示
	//恢复任务栏
	::ShowWindow(::FindWindow(_T("Shell_TrayWnd"), NULL), SW_SHOW);
	dlg.DestroyWindow();
	_endthreadex(0);
	return 0;
}
int LockMachine() {
	//创建子线程
	if ((dlg.m_hWnd == NULL) || (dlg.m_hWnd == INVALID_HANDLE_VALUE)) {
		//没有锁机 或者没有被初始化才能开启子线程
		//_beginthread(threadLockDlg, 0, NULL);
		HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, threadLockDlg, NULL, 0, &threadid);
		TRACE("threadid=%d\r\n", threadid);
		//等待子线程退出
		//WaitForSingleObject(hThread, INFINITE);
		//CloseHandle(hThread);
	}
	CPacket pack(7, NULL, 0);					    //发送应答消息包
	CServerSocket::getInstance()->Send(pack);
	return 0;
}
/*
* 这样做没有限制鼠标和后台操作 没有实现完整的锁机机制
int LockMachine() {
	dlg.Create(IDD_DIALOG_INFO, NULL);		//创建窗口
	dlg.ShowWindow(SW_SHOW);
	dlg.SetWindowPos(&dlg.wndTopMost, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);		//保持原样展示
	MSG msg;		//创建消息
	//消息循环 等待特定按键按下解除锁屏
	while (GetMessage(&msg, NULL, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
		if (msg.message == WM_KEYDOWN) {
			TRACE("msg:%08X wparam:%08X lparam:%08X\r\n", msg.message, msg.wParam, msg.lParam);
			if (msg.wParam == 0x1b)
				break;
		}
	}
	dlg.DestroyWindow();
	return 0;
}
*/

//解锁
int UnlockMachine()
{
	//dlg.SendMessage(WM_KEYDOWN, 0x41, 0x01E0001);
	//::SendMessage(dlg.m_hWnd, WM_KEYDOWN, 0x41, 0x01E0001);
	PostThreadMessage(threadid, WM_KEYDOWN, 0x1b, 0);
	CPacket pack(8, NULL, 0);
	CServerSocket::getInstance()->Send(pack);
	return 0;
}

int DeleteLocalFile()
{
	std::string strPath;
	CServerSocket::getInstance()->GetFilePath(strPath);
	TCHAR sPath[MAX_PATH] = _T("");
	//mbstowcs(sPath, strPath.c_str(), strPath.size()); //中文容易乱码
	MultiByteToWideChar(
		CP_ACP, 0, strPath.c_str(), strPath.size(), sPath,
		sizeof(sPath) / sizeof(TCHAR));
	DeleteFileA(strPath.c_str());
	CPacket pack(9, NULL, 0);
	bool ret = CServerSocket::getInstance()->Send(pack);
	TRACE("Send ret = %d\r\n", ret);
	return 0;
}


//测试网络连接
int TestConnect()
{
	CPacket pack(1981, NULL, 0);
	int ret = pserver->Send(pack);
	TRACE("TestConnect send back ret = %d\r\n", ret);
	return 0;
}

//执行命令
int ExcuteCommand(int nCmd)
{
	int ret = 0;
	switch (nCmd) {
	case 1:
		//查看磁盘分区
		ret = MakeDriverInfo();
		break;
	case 2:
		//查看指定目录下的文件
		ret = MakeDirectoryInfo();
		break;
	case 3:
		//运行文件
		ret = RunFile();
		break;
	case 4:
		//下载文件
		ret = DownloadFile();
		break;
	case 5:
		//鼠标事件处理
		ret = MouseEvent();
		break;
	case 6:
		//屏幕显示图像发送
		//本质是发送屏幕当前截图
		ret = SendScreen();
		break;
	case 7:
		//锁机
		ret = LockMachine();
		//Sleep(10000);

		//LockMachine();
		//Sleep(5000);
		//UnlockMachine();
		break;
	case 8:
		//解锁
		ret = UnlockMachine();
		break;
	case 9:
		//删除文件
		ret = DeleteLocalFile();
		break;
	case 1981:
		//测试连接
		ret = TestConnect();
		break;
	}
	return ret;
	//while (dlg.m_hWnd != NULL && (dlg.m_hWnd != INVALID_HANDLE_VALUE))
	//	Sleep(100);
}
/*************************************main********************************************/
int main()
{
	int nRetCode = 0;

	HMODULE hModule = ::GetModuleHandle(nullptr);

	if (hModule != nullptr)
	{
		// 初始化 MFC 并在失败时显示错误
		if (!AfxWinInit(hModule, nullptr, ::GetCommandLine(), 0))
		{
			// TODO: 在此处为应用程序的行为编写代码。
			wprintf(L"错误: MFC 初始化失败\n");
			nRetCode = 1;
		}
		else
		{

			//// TODO: 在此处为应用程序的行为编写代码。
			//CServerSocket* pserver = CServerSocket::getInstance();
			int count = 0;
			//初始化套接字
			if (pserver->InitSocket() == false)
			{
			    MessageBox(NULL, _T("网络初始化异常，未能成功初始化，请检查网络状态！"), _T("网络初始化失败"), MB_OK | MB_ICONERROR);
			    exit(0);
			}
			while (CServerSocket::getInstance() != NULL)
			{
			    //accept
			    if (pserver->AcceptClient() == false)
			    {
			        if (count >= 3)
			        {
			            MessageBox(NULL, _T("多次无法正常接入用户，结束程序！"), _T("接入用户失败！"), MB_OK | MB_ICONERROR);
			            exit(0);
			        }
			        MessageBox(NULL, _T("无法正常接入用户，自动重试"), _T("接入用户失败！"), MB_OK | MB_ICONERROR);
			        count++;
			    }
			    TRACE("AcceptClient return true\r\n");
			    int ret = pserver->DealCommand();
				TRACE("Server DealCommand No.%d\r\n",ret);
				if (ret > 0)
				{
					ret = ExcuteCommand(ret);
					TRACE("Server ExcuteCommand ret = %d\r\n", ret);
					if (ret != 0) {
						TRACE("执行命令失败, ret = %d\r\n", pserver->GetPacket().sCmd, ret);
					}
					// 长连接——大量数据分发 ＞3次数据收发/s
					// 短连接——小量数据分发
					// 这里采用短连接的方式，每次处理完命令都关闭客户端套接字
					pserver->CloseClient();
					TRACE("Command has done!\r\n");
				}
			    
			}

			
		}
	}
	else
	{
		// TODO: 更改错误代码以符合需要
		wprintf(L"错误: GetModuleHandle 失败\n");
		nRetCode = 1;
	}

	return nRetCode;
}
